package com.zwj.spring_study.a02;

import lombok.Getter;
import lombok.Setter;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;

/**
 * @author 张文接
 * @version 1.0
 * @createTime 2024/5/19 20:20
 */
public class A02Application {
    public static void main(String[] args) {
//        testClassPathXmlApplicationContext();
//        testFileSystemClassPathXmlApplication();
//        testAnnotationConfigApplicationContext();
//        testAnnotationConfigServletWebServerApplicationContext();
    }

    /**
     * 较为经典的容器，基于classpath 下 xml 格式的配置文件来创建
     * 基于类路径下 xml 格式的配置文件来创建容器，容器启动时会自动扫描配置文件，并创建配置文件中定义的 bean
     */
    public static void testClassPathXmlApplicationContext() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:b01.xml");
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }

    /**
     * 基于文件系统下 xml 格式的配置文件来创建容器 与ClassPathXmlApplicationContext类似，读取xml格式的配置文件，创建bean
     */
    public static void testFileSystemClassPathXmlApplication() {
        FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("spring_study\\src\\main\\resources\\b01.xml");
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.out.println(beanDefinitionName);
        }
        System.out.println(context.getBean(Bean2.class).getBean1());
    }

    /**
     * 使用 AnnotationConfigApplicationContext 加载注解配置类
     */
    public static void testAnnotationConfigApplicationContext() {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        Bean2 bean = context.getBean(Bean2.class);
        System.out.println(bean.getBean1());
    }

    /**
     * 使用 AnnotationConfigServletWebServerApplicationContext 加载 web 配置类
     * 容器启动完成后，内嵌的tomcat默认会在8080端口监听，可以访问localhost:8080/hello测试配置是否加载成功
     */
    public static void testAnnotationConfigServletWebServerApplicationContext() {
        ApplicationContext context = new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
    }

    /**
     * 关于ClassPathXmlApplicationContext的案例
     */
    public static void demoForClassPathXmlApplicationContext() {
        //定义一个BeanFactory
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        System.out.println("读取之前。。。。。。。。");
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
        System.out.println("读取之后。。。。。。。。");
        //定义一个BeanDefinition
        XmlBeanDefinitionReader definitionReader = new XmlBeanDefinitionReader(beanFactory);
        //加在到BeanFactory中
        definitionReader.loadBeanDefinitions(new ClassPathResource("b01.xml"));
        for (String name : beanFactory.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    @Configuration
    static class Config {
        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2(Bean1 bean1) {
            Bean2 bean2 = new Bean2();
            bean2.setBean1(bean1);
            return bean2;
        }
    }

    static class Bean1 {
    }

    @Getter
    @Setter
    static class Bean2 {
        private Bean1 bean1;
    }

    @Configuration
    static class WebConfig {
        // 配置 web 容器
        @Bean
        public ServletWebServerFactory servletWebServerFactory() {
            return new TomcatServletWebServerFactory();
        }

        // 配置前控制器
        @Bean
        public DispatcherServlet dispatcherServlet() {
            return new DispatcherServlet();
        }

        // 注册【前控制器】到 web 容器
        @Bean
        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
            // "/" 匹配所有请求
            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
        }

        // 手动注册一个控制器，这相当于使用 @Controller 注解注册了一个控制器
        @Bean("/hello") // 如果 bean 名称以 / 开头, 那么此控制器的访问路径就为完整的 bean 名称
        public Controller controllerOne() {
            return (request, response) -> {
                response.getWriter().println("hello");
                return null;
            };
        }
    }

}
